package modeller.queueingnetworksmodel.structure;

import modeller.queueingnetworksmodel.probabilities.TransactionRoutingMatrix;
import modeller.databasedesignmodel.relation.Relation;
import modeller.databasedesignmodel.transaction.*;
import org.jdom2.Element;

import java.util.Set;

import static queped.quepedconstants.Strings.*;

/**
 * Created by:  Jason Ye
 * Date:        30/05/2012
 * Time:        20:59
 */
public class RouterElement extends AbstractNodeChildElement {

    private TransactionRoutingMatrix qnInstantaneousTransitionMatrix;
    private Relation sink;

    public RouterElement(TransactionRoutingMatrix qnInstantaneousTransitionMatrix, Relation relation) {

        super(qnInstantaneousTransitionMatrix.getTransactions(),relation);
        this.qnInstantaneousTransitionMatrix = qnInstantaneousTransitionMatrix;
        this.sink = new Relation(2,2,2,2);
        sink.setRelationName("Sink 0");

    }

    @Override
    public Element toXMLRepresentation() {

        Element router = new Element(XML_E_STATION_SECTION);

        router.setAttribute(XML_A_STATION_SECTION_CLASSNAME, CLASSNAME_ROUTER);

        // parameters

        // routingStrategy = list of refClass and subParameter for each transaction
        // subparameter holds the destination relation name and the associated pi
        Element routingStrategy = generateRoutingStrat();

        router.addContent(routingStrategy);

        return router;

    }

    private Element generateRoutingStrat() {

        Element routingStrat = generateNewParam(JMTROUTINGSTRAT, XML_A_ROUTINGSTRATEGY, true);

        Set<Relation> destRelations = qnInstantaneousTransitionMatrix.getMapRelationToIndex().keySet();

        // for each transaction
        for (Transaction transaction : transactions) {

            Element probabilitiesForTransaction = generateSubParam(JMTEMPIRCALSTRAT, XML_A_SUBPARAMETER_PROBABILITIES);

            boolean hasProbability = false;

            Element empiricalEntryArray = generateSubParam(JMTEMPIRCALENTRY, XML_A_SUBPARAMETER_EMPIRICAL_ENTRYARRAY, true);

            // for all possible relations
            for (Relation dest : destRelations ) {

                // if transition prob !=0 then add something
                double pi = qnInstantaneousTransitionMatrix.getTransitionProbability(transaction, relation, dest);

                if (pi > 0) {

                    // tell the loop outsider to add a empirical entry array for this transaction
                    hasProbability = true;

                    if(transaction.isOpen() && dest.getRelationName().equals("Router")){

                    dest = sink;
                    }
                        empiricalEntryArray.addContent(generateEmpiricalEntryElem(dest, pi));

                }

            }

            if (hasProbability) {
                Element transactionRefClass = generateNewRefClassElem(transaction.getTransactionName());

                probabilitiesForTransaction.addContent(empiricalEntryArray);
                routingStrat.addContent(transactionRefClass);
                routingStrat.addContent(probabilitiesForTransaction);
            }

        }
        return routingStrat;
    }

    private Element generateEmpiricalEntryElem(Relation dest, double pi) {

        //init element
        Element empiricalEntry = generateSubParam(JMTEMPIRCALENTRY, XML_A_SUBPARAMETER_EMPIRICAL_ENTRY);

        //station name
        empiricalEntry.addContent(generateEmpiricalEntryStationName(dest));
        //probability
        empiricalEntry.addContent(generateEmpiricalEntryProbElem(pi));
        return empiricalEntry;
    }

    private Element generateEmpiricalEntryProbElem(double pi) {

        Element p = generateSubParam(DOUBLECLASSPATH, XML_A_SUBPARAMETER_PROBABILITY);

        p.addContent(generateValueElem(Double.toString(pi)));

        return p;
    }

    private Element generateEmpiricalEntryStationName(Relation dest) {

        Element stationName = generateSubParam(STRINGCLASSPATH, XML_A_PRELOADSTATION_NAME);

        Element value = generateValueElem(dest.getRelationName());

        stationName.addContent(value);

        return stationName;


    }

}